import { observe } from '../observer/index'
import { isPlainObject, noop } from '../../util/index'
import { Watcher } from '../observer/watcher'
import { Dep } from '../observer/dep'

export function stateMixin(Vue) {
    Vue.prototype.$watch = function (
        expOrFn,
        cb,
        options
    ) {
        const vm = this
        if (isPlainObject(cb)) { 
            //回调函数可以是个对象{  handle: fn } 这种类型
            //使用createWatcher处理一下格式
          return createWatcher(vm, expOrFn, cb, options)
        }
        options = options || {}
        options.user = true //用户自定义watchers
        const watcher = new Watcher(vm, expOrFn, cb, options)
        if (options.immediate) {
            try {
                cb.call(vm, watcher.value)
            } catch (error) {
                console.error('immediate求职异常');
            }
        }
    }
}

export function initState(vm) {
    vm._watchers = []
    const opts = vm.$options
    if (opts.props) {
        initProps(vm, opts.props)
    }
    if (opts.methods) {
        initMethods(vm, opts.methods)
    }
    if (opts.data) {
        initData(vm)
    } else {
        observe(vm._data = {})
    }
    if (opts.computed) {
        initComputed(vm, opts.computed)
    }
    if (opts.watch) {
        initWatch(vm, opts.watch)
    }
}
function initData(vm) {
    let data = vm.$options.data
    data = vm._data  = typeof data === 'function'
        ? data.call(vm, vm)
        : data || {}
    if (!isPlainObject(data)) {
        data = {}
        console.error('传入的data属性应为object类型或者一个返回object数据类型的函数')
    }
    const keys = Object.keys(data)
    let i = keys.length
    while (i--) {
        const key = keys[i]
        Object.defineProperty(vm, key, {
            get: function proxyGetter() {
                return this._data[key]
            },
            set: function proxySetter(val) {
                this._data[key] = val
            }
        })
    }
    vm._data  = observe(data)
}
function initMethods(vm, methods) {
    for (const key in methods) {
        vm[key] = typeof methods[key] !== 'function' ? noop : methods[key].bind(vm)
    }
}
function initComputed(vm, computed) {
    const watchers = vm._computedWatchers = Object.create(null)
    for (const key in computed) {
        const userDef = computed[key]
        const getter = typeof userDef === 'function' ? userDef : userDef.get
        watchers[key] = new Watcher(
            vm,
            getter || noop,
            noop,
            { lazy: true }
        )

        if (!(key in vm)) {
            Object.defineProperty(vm, key, {
                get: function () {
                    return createComputedGetter(vm, key)
                },
                set: noop
            })
        }
    }
}
function createComputedGetter(vm, key) {
    const watcher = vm._computedWatchers && vm._computedWatchers[key]
    if (watcher) {
        if (watcher.dirty) {
            //更新收集者
            watcher.evaluate()
        }
        if (Dep.target) {
            //重新收集
            watcher.depend()
        }
        return watcher.value
    }
}
function initProps(vm, propsOptions) {
    const propsData = vm.$options.propsData || {}
    const keys = vm.$options._propKeys = []
    for (const key in propsOptions) {
        keys.push(key)
        propsOptions[key] = propsData[key]
        if (!(key in vm)) {
            Object.defineProperty(vm, key, {
                get: function proxyGetter() {
                    return this._props[key]
                },
                set: function proxySetter(val) {
                    this._props[key] = val
                }
            })
        }
    }
    vm._props = observe(propsOptions)
}
function initWatch(vm, watch) {
    for (const key in watch) {
        const handler = watch[key]
        if (Array.isArray(handler)) {
            for (let i = 0; i < handler.length; i++) { //回调函数是数组类型
                createWatcher(vm, key, handler[i])
            }
        } else {
            createWatcher(vm, key, handler)
        }
    }
}

function createWatcher (
    vm,
    expOrFn,
    handler,
    options
  ) {
    if (isPlainObject(handler)) {
      options = handler
      handler = handler.handler
    }
    if (typeof handler === 'string') { //回调函数字符串
      handler = vm[handler]
    }
    return vm.$watch(expOrFn, handler, options)
  }